<?php

namespace Theatre;

class Controller {
	/**
	 * @var $url The current URL
	 */
	private $url;


	/**
	 * @var $params the requested params
	 */
	private $params;


	/**
	 * @var $action
	 */
	public $action;


	/**
	 * @var $name
	 */
	private $name;


	/**
	 * @var $config The app config
	 */
	public $config;


	/**
	 * @var $noTemplate If a template should be rendered
	 */
	private $noTemplate = true;


	/**
	 * @var models Model references
	 */
	protected $_models = array();


	/**
	 * @var views Views references
	 */
	protected $_views = array();


	/**
	 * Current view
	 */
	protected $_currentView;


	/**
	 * The current template
	 */
	public $template;


	/**
	 * @var viewVars
	 */
	public $viewVars = array();


	/**
	 * Controller body
	 */
	protected $body;


	/**
	 * Base
	 */
	public $base;

	function __construct($config, $controller = null) {
		if ($controller) {
			$this->url     = $controller->url;
			$this->params  = $controller->params;
			$this->action  = $controller->action;
			$this->config  = $controller->config;
			$this->_models = $controller->_models;
			$this->_views  = $controller->_views;
			$this->base    = $controller->base;
		}
		else {
			$this->config = $config;
			if (empty($this->config['sessionKey'])) {
				$this->config['sessionKey'] = 'theatre';
			}

			if (!empty($this->config['useSession'])) {
				$this->session = new \Theatre\Session($this->model('PostGreSQL'), $this->config['sessionKey']);
			}
		}
	}


	/**
	 * Destructor
	 */
	function __destruct() {
	}


	/**
	 * Render the template
	 */
	function render() {
		if (empty($this->_currentView)) {
			$this->view('HTML');
		}

		return $this->_views[$this->_currentView]->render($this);
	}


	function default_404() {
		$this->noTemplate = true;
		$this->body = 'Page not found';
	}


	/**
	 * Set the view vars
	 */
	function set($data) {
                $this->viewVars = $data + $this->viewVars;
	}


	/**
	 * Call the model
	 */
	public function model($modelName) {
		if (empty($this->_models[$modelName])) {
			// load the model
			$modelFile = $this->config['docRoot'] . "/Theatre/Model/{$modelName}.php";
			if (!file_exists($modelFile)) {
				throw new \Exception("Cannot find model {$modelFile}");
			}

			$class = "\\Theatre\\Model\\{$modelName}";
			if (!class_exists($class)) {
				throw new \Exception("Cannot find class {$class}");
			}

			$constructorData = null;
			if ($modelName == 'PostGreSQL') {
				$constructorData = $this->config['db'];
			}
			elseif ($modelName == 'TicketAPI') {
				$loggedInUser = $this->getLoggedInUser();

				if (empty($loggedInUser)) {
					throw new \Exception("User not logged in");
				}

				$constructorData = array(
					'api_key'     => $loggedInUser->api_key,
					'partner_id'  => $loggedInUser->partner_id,
					'user_id'     => $loggedInUser->id,
					'server_host' => $this->config['serverHost'],
					'use_ssl'     => empty($this->config['use_ssl']) ? false : true
				);
			}

			$this->_models[$modelName] = new $class($constructorData);
		}

		return $this->_models[$modelName];
	}


	/**
	 * Call the view
	 */
	public function view($viewName) {
		if (empty($this->_views[$viewName])) {
			// load the view
			$viewFile = $this->config['docRoot'] . "/Theatre/View/{$viewName}.php";
			if (!file_exists($viewFile)) {
				throw new \Exception("Cannot find view {$viewFile}");
			}

			$class = "\\Theatre\\View\\{$viewName}";

			if (!class_exists($class)) {
				throw new \Exception("Cannot find class {$class}");
			}

			$this->_views[$viewName] = new $class();
		}

		$this->_currentView = $viewName;
	}


	/**
	 * Disable cache
	 */
	public function disableCache() {
		header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
                header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
                header("Cache-Control: no-store, no-cache, must-revalidate");
                header("Cache-Control: post-check=0, pre-check=0", false);
                header("Pragma: no-cache");
	}

	/**
	 * Load other controller
	 */
	private function getController($controllerClass) {
		// name path representation
		$namespacePath = str_replace('\\', '/', $controllerClass);

		// find out if the controller exists
		// if it does, load it and instantiate it
		$controllerPath = $this->config['docRoot'] . '/' . $namespacePath . ".php";
		if (!file_exists($controllerPath)) {
			throw new \Exception("Missing conroller file {$controllerPath}");
		}
	
		if (!class_exists($controllerClass)) {
			throw new \Exception("Missing conroller class {$controllerClass}");
		}

		return new $class(null, $this);
	}


	/**
	 * Invoke action
	 */
	public function invoke() {
		$this->beforeFilter();
                $this->{$this->action}();
                $this->beforeRender();	
	}


	/**
	 * Authenticate
	 */
	protected function authenticate($data) {
		if (empty($data['username']) || empty($data['password'])) {
			return false;
		}

		$authedUser = $this->model('PostGreSQL')->dbRun("authenticateClient($1, $2)", $data['username'], $data['password']);

		if (!empty($authedUser) && $authedUser->authenticateclient) {
			// renew the session
			$this->session->resetSessionId();

			// addd the session
			$_SESSION[ $this->config['sessionKey'] ] = $authedUser->authenticateclient;

			return true;
		}

		return false;
	}


	/**
	 * Logout
	 */
	protected function _logout() {
		if ($_SESSION[ $this->config['sessionKey'] ]) {
			unset($_SESSION[ $this->config['sessionKey'] ]);
		}
	}


	/**
	 * Before filter callback
	 */
	protected function beforeFilter() {
	}


	/**
	 * Before render callback
	 */
	protected function beforeRender() {
	}


	/**
	 * async request
	 */
	public function asyncRequest() {
		return !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
	}


	/**
	 * form submitted
	 */
	public function formSubmitted() {
		return !empty($_POST);
	}


	/**
	 * Get the logged in User
	 */
	protected function getLoggedInUser() {
		$user = null;

		if (!empty($_SESSION[ $this->config['sessionKey'] ])) {
			$user = $this->model('PostGreSQL')->dbRun('getUserById($1)', $_SESSION[ $this->config['sessionKey'] ]);
		}

		return $user;
	}


	/**
	 * Check if the user exists
	 */
	protected function userExists() {
		return empty($_SESSION[ $this->config['sessionKey'] ]) ? false : true;
	}


	/**
	 * Redirect
	 */
	protected function redirect($url) {
		if ($this->asyncRequest()) {
			$this->set(array('_redirect' => $this->base . $url));
		}
		else {
			header("Location: {$this->base}{$url}");

			exit();
		}
	}


	/**
	 * Check if the API request is valid
	 */
	protected function APIRequestIsValid($params) {
		if (empty($params)) {
			return array(
				'status'  => 'error',
				'message' => 'Empty request'
			);
		}

		if (empty($params['hash'])) {
			return array(
				'status'  => 'error',
				'message' => 'No hash'
			);
		}

		if (empty($params['user_id'])) {
			return array(
				'status'  => 'error',
				'message' => 'No user ID'
			);
		}

		if (empty($params['partner_id'])) {
			return array(
				'status'  => 'error',
				'message' => 'No partned ID'
			);
		}

		if (empty($params['time'])) {
			return array(
				'status'  => 'error',
				'message' => 'No time attached'
			);
		}

		// reply attacks
		if ($params['time'] < time() - 20) {
			return array(
				'status'  => 'error',
				'message' => 'Message expired. Slow network or some kind of trickery?'
			);
		}
		else {
			// check the hash for reply attacks, might be already sent
			$hash = $this->model('PostGreSQL')->dbRun('hashExpired($1)', $params['hash']);

			if (empty($hash)) {
				return array(
					'status'  => 'error',
					'message' => 'Server error'
				);
			}

			if ($hash->hashexpired == 't') {
				return array(
					'status'  => 'error',
					'message' => 'Hash expired'
				);
			}
		}

		$partner = $this->model('PostGreSQL')->dbRun('getPartnerById($1)', $params['partner_id']);

		if (empty($partner)) {
			return array(
				'status'  => 'error',
				'message' => 'Partner not found'
			);
		}

		$hash       = $params['hash'];
		$user_id    = $params['user_id'];
		$partner_id = $params['partner_id'];
		$time       = $params['time'];

		unset($params['hash']);
		unset($params['time']);
		if (!empty($params['url'])) {
			unset($params['url']);
		}

		$computedHash = hash_hmac('sha1', json_encode($params) . $time . $user_id . $partner_id, $partner->api_key);

		if ($computedHash != $hash) {
			return array(
				'status'  => 'error',
				'message' => 'Invalid Hash ' . $computedHash .'   ' . $hash
			);
		}

		return array(
			'status'  => 'success',
			'partner' => $partner
		);
	}
}
